#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# shared objects (in globals()):
#     myMplGuiMain: MyMplGuiMain
#     myMplObjectGw: MplObjectGateway
#     plotterDateTimes: list<datetimeStr>
#     plotterDateTimeIdx: list<int>
#     mplCursor: MyMultiCursor
#

import time
from threading import Thread
import queue
import traceback
import logging
import matplotlib.pyplot as plt
from matplotlib import ticker
from matplotlib.widgets import Cursor, MultiCursor
import matplotlib.style as mplstyle; mplstyle.use("fast")


# jep globals:
jepLogFile = ""
logLevel = "DEBUG"
logFormat = "%(asctime)s %(name)s %(levelname)s [%(threadName)s] - %(message)s (%(filename)s, line: %(lineno)d)"
logger = None
# plotter globals:
mplCursor = None  # mplCursor: MyMultiCursor


def initLogging():
    """
    :return:
    """
    global logger
    if logger is not None:
        global jepLogFile, logLevel, logFormat
        logging.basicConfig(filename=jepLogFile,
                            format=logFormat,
                            level=logLevel)
        logger = logging.getLogger()


class MyCursor(Cursor):
    """
    """
    def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, **lineprops):
        super(MyCursor, self).__init__(ax, horizOn, vertOn, useblit, **lineprops)

    def onmove(self, event):
        """event:  class matplotlib.backend_bases.MouseEvent
        """
        super(MyCursor, self).onmove(event)


class MyMultiCursor(MultiCursor):
    """
    """
    def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True, **lineprops):
        super(MyMultiCursor, self).__init__(canvas, axes, useblit, horizOn, vertOn, **lineprops)

    def onmove(self, event):
        """event:  class matplotlib.backend_bases.MouseEvent
        """
        super(MyMultiCursor, self).onmove(event)


def formatDateMin(x, pos=None):
    """global: plotterDateTimes
    """
    if x < 0 or x > len(plotterDateTimes) - 1:
        return ""
    else:
#       return plotterDateTimes[int(x)][0:8]
        return plotterDateTimes[int(x)]


class MyMplGuiMain(Thread):
    """use proxy pattern to redirect calling to mpl object in the mpl main gui thread:
    """
    def __init__(self, nrows, pltPause=0.5, debug=True):
        """
        """
        super(MyMplGuiMain, self).__init__()
        # params:
        self.__nrows = nrows
        self.__pltPause = pltPause
        self._debug = debug
        # proxy call args queue:
        self._queue = queue.Queue()
        # mpl objects:
        self.__fig = None
        self.__axes = None
        self.__axesLs = None

    def run(self):
        """
        """
        plt.ion()
        self.__fig, self.__axes = plt.subplots(nrows=self.__nrows, sharex=True, squeeze=False)
        self.__axesLs = self.__axes.flatten().tolist()
        global mplCursor
        mplCursor = MyMultiCursor(self.__fig.canvas, self.__axesLs,
                                  useblit=True, horizOn=False, vertOn=True, color="gray", linestyle="-.", linewidth=1.0)
        while True:
            try:
                servant, method, args, kwargs = self._queue.get(block=False)
                getattr(servant, method)(*args, **kwargs)
            except queue.Empty:
                pass
            except Exception as ex:
                global logger
                if logger is not None:
                    logger.debug("exception occurred (servant: %s, method: %s, args: %s, kwargs: %s): %s" %
                                 (servant, method, args, kwargs, traceback.format_exc()))
                else:
                    print("exception occurred (servant: %s, method: %s, args: %s, kwargs: %s): %s" %
                          (servant, method, args, kwargs, traceback.format_exc()))
                if not self._debug:
                    raise ex
            finally:
                plt.pause(self.__pltPause)
#                time.sleep(self.__pltPause)

    def getFig(self):
        """
        """
        return self.__fig

    def getAxesLs(self):
        """
        """
        return self.__axesLs


class MplObjectGateway(object):
    """
    """
    def __init__(self, host: MyMplGuiMain):
        """
        :param host:
        """
        self.__host = host

    def invoke(self, servant: object, method: str = "", *args, **kwargs):
        """
        :param servant:
        :param method:
        :param args:
        :param kwargs:
        :return:
        """
        assert len(method) > 0
        self.__host._queue.put((servant, method, args, kwargs))
